package com.zhu.rtda.heap;

import com.zhu.classfile.MemberInfo;
import com.zhu.classfile.attribute.ConstantValueAttribute;

/**
 * Field 表示 Java 类中的一个字段（成员变量）。
 * 它包含字段的元数据信息，如名称、类型、访问标志、常量值索引和在变量槽中的位置。
 * 该类的设计直接对应 class 文件中的字段表（fields[]）条目。
 */
public class Field extends ClassMember {

    /**
     * 如果该字段是静态常量（static final 基本类型或 String），
     * 则此字段记录其在常量池中的索引，用于类初始化时赋值。
     * 若该字段不是常量，则值为 0。
     */
    public int constValueIndex;

    /**
     * 该字段在所属类的变量槽（Slots）数组中的位置索引。
     * - 静态字段：位于类的静态变量槽（staticVars）中
     * - 实例字段：位于对象的实例变量槽（fields）中
     * 该值在类加载的准备阶段由类加载器分配。
     */
    public int slotId;

    /**
     * 从 ClassBinary 中的字段信息数组创建字段数组。
     * 该方法用于类加载过程中，将字节码中的字段信息转换为运行时可用的 Field 对象数组。
     *
     * @param clazz      字段所属的类
     * @param cfFields   ClassBinary 中的字段信息数组（来自字段表）
     * @return           创建的 Field 对象数组
     */
    public static Field[] newFields(InstanceKlass clazz, MemberInfo[] cfFields) {
        Field[] fields = new Field[cfFields.length];
        for (int i = 0; i < cfFields.length; i++) {
            fields[i] = new Field();
            //从这里可以看出字段和运行时类是双向关联的
            fields[i].clazz = clazz;
            fields[i].copyMemberInfo(cfFields[i]);
            fields[i].copyAttributes(cfFields[i]);
        }
        return fields;
    }

    /**
     * 从字段的 class 文件信息中复制属性相关数据。
     * 目前只处理 ConstantValue 属性，用于 static final 字段的初始化。
     *
     * @param cfField 字节码中的字段信息
     */
    private void copyAttributes(MemberInfo cfField) {
        ConstantValueAttribute attr = cfField.getConstantValueAttribute();
        if (attr != null) {
            this.constValueIndex = attr.getConstantValueIndex();
        }
    }

    /**
     * 判断该字段是否为 volatile 字段。
     * volatile 字段保证可见性和有序性，常用于多线程环境。
     *
     * @return 如果是 volatile 字段则返回 true
     */
    public boolean isVolatile() {
        return (accessFlags & AccessFlags.ACC_VOLATILE.getValue()) != 0;
    }


    /**
     * 判断该字段是否为 transient 字段。
     * transient 字段在对象序列化时会被忽略。
     *
     * @return 如果是 transient 字段则返回 true
     */
    public boolean isTransient() {
        return (accessFlags & AccessFlags.ACC_TRANSIENT.getValue()) != 0;
    }

    /**
     * 判断该字段是否是枚举常量（enum constant）。
     * 枚举类中的每个枚举值都是一个 public static final 字段，
     * 且会被标记 ACC_ENUM。
     *
     * @return 如果是枚举常量则返回 true
     */
    public boolean isEnum() {
        return (accessFlags & AccessFlags.ACC_ENUM.getValue()) != 0;
    }

    /**
     * 判断该字段是否是静态变量
     * @return
     */
    public  boolean isStatic(){return (accessFlags & AccessFlags.ACC_STATIC.getValue()) != 0;}
    /**
     * 获取该字段的常量值在常量池中的索引。
     * 仅当该字段是 static final 基本类型或 String 时有效。
     *
     * @return 常量池索引，若无则返回 0
     */
    public int getConstValueIndex() {
        return constValueIndex;
    }

    /**
     * 获取该字段在变量槽（Slots）数组中的位置索引。
     *
     * @return 槽位索引
     */
    public int getSlotId() {
        return slotId;
    }

    public int setSlotId(int slotId) {
        return this.slotId=slotId;
    }

    /**
     * 判断该字段是否为 long 或 double 类型。
     * 这两种类型的字段在 Slots 数组中占用两个槽位（double slot），
     * 其余类型占用一个槽位。
     *
     * @return 如果是 long 或 double 类型则返回 true
     */
    public boolean isLongOrDouble() {
        return "J".equals(descriptor) || "D".equals(descriptor);
    }

    // ========== 反射支持 ==========

    /**
     * 获取该字段的类型所对应的类对象。
     * 用于反射机制，例如 Field.getType()。
     *
     * @return 字段类型的类对象（Class 实例）
     */
//    public Class getType() {
//        String className = toClassName(descriptor);
//        return clazz.getClassLoader().loadClass(className);
//    }

    /**
     * 将字段描述符转换为类的全限定名。
     * 例如：
     *   "I"        -> "int"
     *   "Ljava/lang/String;" -> "java/lang/String"
     *   "[I"       -> "[I"
     *   "[[Ljava/lang/Object;" -> "[[Ljava/lang/Object;"
     *
     * @param descriptor 字段描述符
     * @return 类的全限定名（内部格式）
     */
    private String toClassName(String descriptor) {
        switch (descriptor.charAt(0)) {
            case 'B': return "byte";
            case 'C': return "char";
            case 'D': return "double";
            case 'F': return "float";
            case 'I': return "int";
            case 'J': return "long";
            case 'S': return "short";
            case 'Z': return "boolean";
            case 'L':
                // 对象类型：Ljava/lang/String; -> java/lang/String
                return descriptor.substring(1, descriptor.length() - 1);
            case '[':
                // 数组类型：[I -> [I, [[Ljava/lang/Object; -> [[Ljava/lang/Object;
                return descriptor;
            default:
                throw new RuntimeException("Invalid field descriptor: " + descriptor);
        }
    }
}
